using System;
using System.Collections.Generic;
using DevExpress.Data.Filtering;
using DevExpress.Xpo;
using SmartPeople.Framework.SecuritySpace;
using SmartPeople.Framework.SecuritySpace;

namespace SmartPeople.Framework.Security.Providers.XPO
{
    /// <summary>
    /// 
    /// </summary>
    public class XPSecurityUserProvider : ISecurityUsersProvider
    {
        private string applicationID;

        public XPSecurityUserProvider()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="XPSecurityUserProvider"/> class.
        /// </summary>
        /// <param name="applicationID">The application ID.</param>
        public XPSecurityUserProvider(string applicationID) : this()
        {
            this.applicationID = applicationID;
        }

        /// <summary>
        /// Gets the count.
        /// </summary>
        /// <value>The count.</value>
        public int Count
        {
            get
            {
                XPCollection<XPSecurityUser> xpCollection = new XPCollection<XPSecurityUser>();
                return xpCollection.Count;
            }
        }

        #region  Old Authenticate Commented (For Reference)

        //public IToken Authenticate(string userName, string password)
        //{
        //    XPSecurityUser securityUser = (XPSecurityUser) GetUser(userName, false);
        //    if ((securityUser == null) || (securityUser.IsApproved == false))
        //        return null;

        //    if (securityUser.Password == password)
        //    {
        //        Guid tokenGuid = Guid.NewGuid();
        //        IToken token = new GuidToken(tokenGuid);
        //        securityUser.SessionID = tokenGuid;
        //        securityUser.LastActivityDate = DateTime.Now;
        //        securityUser.LastLoginDate = DateTime.Now;
        //        securityUser.IsOnline = true;
        //        securityUser.Save();
        //        return token;
        //    }
        //    return null;
        //}

        #region Authenticate(string userName, string password)

        /// <summary>
        /// Authenticates the specified user name.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        /// <returns></returns>
        public IToken Authenticate(string userName, string password)
        {
            XPSecurityUser securityUser = (XPSecurityUser) GetUser(userName, false);

            if ((securityUser == null) || (securityUser.IsApproved == false) || (securityUser.IsLockedOut == true))
                return null;

            if (securityUser.UserName == userName & securityUser.Password != password)
            {
                noofTriesCount = noofTriesCount + 1;
                SecurityPolicy secpolicy = new SecurityPolicy();
                if (noofTriesCount == secpolicy.NumberofTries)
                {
                    securityUser.IsLockedOut = true;
                    securityUser.LastLockoutDate = DateTime.Now;
                    UpdateUser(securityUser);
                    noofTriesCount = 0;
                }
            }


            if (securityUser.Password == password)
            {
                Guid guidToken = Guid.NewGuid();
                IToken sessionUsertoken = new GuidToken(guidToken);
                //set session id token (guid)
                securityUser.SessionID = guidToken;
                securityUser.LastActivityDate = DateTime.Now;
                securityUser.LastLoginDate = DateTime.Now;
                //set online true as user is now online
                securityUser.IsOnline = true;
                securityUser.Save();
                return sessionUsertoken;
            }
            return null;
        }

        #endregion

        #endregion

        //TODO:Check why we need IP Address in Authenticate ???

        private int noofTriesCount = 0;

        #region Authenticate(string userName, string password, string ipAddress, DateTime creationDate,int timeout, DateTime lastactive)

        /// <summary>
        /// Authenticates the specified user name.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        /// <param name="ipAddress">The IP address.</param>
        /// <param name="creationDate">The creation date.</param>
        /// <param name="timeout">The timeout.</param>
        /// <param name="lastactive">The lastactive.</param>
        /// <returns></returns>
        public IToken Authenticate(string userName, string password, string ipAddress, DateTime creationDate,
                                   int timeout, DateTime lastactive)
        {
            XPSecurityUser securityUser = (XPSecurityUser) GetUser(userName, false);

            if ((securityUser == null) || (securityUser.IsApproved == false))
                return null;

            if (securityUser.UserName == userName & securityUser.Password != password)
            {
                noofTriesCount = noofTriesCount + 1;
                SecurityPolicy secpolicy = new SecurityPolicy();
                if (noofTriesCount == secpolicy.NumberofTries)
                {
                    securityUser.IsLockedOut = true;
                    securityUser.LastLockoutDate = DateTime.Now;
                    UpdateUser(securityUser);
                    noofTriesCount = 0;
                }
            }


            if (securityUser.Password == password)
            {
                Guid guidToken = Guid.NewGuid();
                IToken token = new GuidToken(guidToken);
                //set session id token (guid)
                securityUser.SessionID = guidToken;
                securityUser.LastActivityDate = DateTime.Now;
                securityUser.LastLoginDate = DateTime.Now;
                securityUser.IsOnline = true;
                securityUser.Save();
                //NOTE:Below line is commented as session concept is not used now.
                //CreateSession(securityUser.GID, IPAddress, creationDate, timeout, lastactive);
                return token;
            }
            return null;
        }

        #endregion

        #region CreateSession(Guid userguid, string ipadress, DateTime creationDate, int timeOut,DateTime lastActive)

        /// <summary>
        /// Creates the session.
        /// </summary>
        /// <param name="userguid">The userguid.</param>
        /// <param name="ipAdress">The ipAdress.</param>
        /// <param name="creationDate">The creation date.</param>
        /// <param name="timeOut">The time out.</param>
        /// <param name="lastActive">The last active.</param>
        public void CreateSession(Guid userguid, string ipAdress, DateTime creationDate, int timeOut,
                                  DateTime lastActive)
        {
            XPSecuritySessionProvider sessionprovider = new XPSecuritySessionProvider();
            sessionprovider.CreateSession(userguid, ipAdress, creationDate, timeOut, lastActive);
        }

        #endregion

        #region CreateUser(string userName, string password, string email)

        /// <summary>
        /// Creates the user.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        /// <param name="email">The email.</param>
        /// <returns></returns>
        public ISecurityUser CreateUser(string userName, string password, string email)
        {
            XPSecurityUser securityUser = new XPSecurityUser();
            securityUser.UserName = userName;
            securityUser.Password = password;
            securityUser.Email = email;
            securityUser.SessionID = Guid.NewGuid();
            //when created normally the user is online is set default to false
            securityUser.IsOnline = false;
            //give a default session id for now
            securityUser.SessionID = Guid.NewGuid();
            //when created the user cannot be created locked out
            securityUser.IsLockedOut = false;
            securityUser.CreationDate = DateTime.Now;
            securityUser.Save();
            return securityUser;
        }

        public ISecurityUser CreateUser(string userName, string password, string email, string passwordQuestion,
                                        string passwordAnswer)
        {
            XPSecurityUser securityUser = new XPSecurityUser();
            securityUser.UserName = userName;
            securityUser.Password = password;
            securityUser.Email = email;
            securityUser.PasswordQuestion = passwordQuestion;
            securityUser.PasswordAnswer = passwordAnswer;
            securityUser.Email = email;
            //create a default session guid for user
            securityUser.SessionID = Guid.NewGuid();
            //when created normally the user is online is set default to false
            securityUser.IsOnline = false;
            //when created the user cannot be created locked out
            securityUser.IsLockedOut = false;
            //set creation date
            securityUser.CreationDate = DateTime.Now;
            securityUser.Save();
            return securityUser;
        }

        #endregion

        #region CreateUser(string userName, string password, string email, string passwordQuestion,string passwordAnswer, bool isApproved, string comment)

        /// <summary>
        /// Creates the user.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="password">The password.</param>
        /// <param name="email">The email.</param>
        /// <param name="passwordQuestion">The password question.</param>
        /// <param name="passwordAnswer">The password answer.</param>
        /// <param name="isApproved">if set to <c>true</c> [is approved].</param>
        /// <param name="comment">The comment.</param>
        /// <returns></returns>
        public ISecurityUser CreateUser(string userName, string password, string email, string passwordQuestion,
                                        string passwordAnswer, bool isApproved, string comment)
        {
            XPSecurityUser securityUser = new XPSecurityUser();
            securityUser.UserName = userName;
            securityUser.Password = password;
            securityUser.Email = email;
            securityUser.PasswordQuestion = passwordQuestion;
            securityUser.PasswordAnswer = passwordAnswer;
            securityUser.Email = email;
            securityUser.IsApproved = isApproved;
            securityUser.Comment = comment;
            //create a default session guid for user
            securityUser.SessionID = Guid.NewGuid();
            //when created normally the user is online is set default to false
            securityUser.IsOnline = false;
            //when created the user cannot be created locked out
            securityUser.IsLockedOut = false;
            //set creation date
            securityUser.CreationDate = DateTime.Now;
            securityUser.Save();
            return securityUser;
        }

        public ISecurityUser CreateUser(string userName, string password, string email, string passwordQuestion,
                                        string passwordAnswer, bool isApproved, string comment, string description,
                                        bool isLockedOut)
        {
            XPSecurityUser securityUser = new XPSecurityUser();
            securityUser.UserName = userName;
            securityUser.Password = password;
            securityUser.Email = email;
            securityUser.PasswordQuestion = passwordQuestion;
            securityUser.PasswordAnswer = passwordAnswer;
            securityUser.Email = email;
            securityUser.IsApproved = isApproved;
            securityUser.Comment = comment;
            //create a default session guid for user
            securityUser.SessionID = Guid.NewGuid();
            //when created normally the user is online is set default to false
            securityUser.IsOnline = false;
            //when created the user cannot be created locked out
            securityUser.IsLockedOut = isLockedOut;
            //set creation date
            securityUser.CreationDate = DateTime.Now;
            securityUser.Description = description;
            securityUser.Save();
            return securityUser;
        }

        #endregion

        //TODO: Check pupose of CreateUser(Guid userGuid)

        #region CreateUser(Guid userGuid)

        /// <summary>
        /// Creates the user.
        /// </summary>
        /// <param name="userGuid">The user GUID.</param>
        /// <returns></returns>
        public ISecurityUser CreateUser(Guid userGuid)
        {
            XPSecurityUser securityUser = new XPSecurityUser();
            securityUser.GID = userGuid;
            securityUser.SessionID = Guid.NewGuid();

            securityUser.CreationDate = DateTime.Now;
            securityUser.IsApproved = false;
            securityUser.IsOnline = false;
            securityUser.IsLockedOut = false;

            securityUser.Save();
            return securityUser;
        }

        #endregion

        #region GetUser

        /// <summary>
        /// Gets the user.
        /// </summary>
        /// <param name="GID">The GID.</param>
        /// <param name="userIsOnline">if set to <c>true</c> [user is online].</param>
        /// <returns></returns>
        private ISecurityUser GetUser(Guid GID, bool userIsOnline)
        {
            CriteriaOperator gidCriteria = new BinaryOperator("GID", GID, BinaryOperatorType.Equal);
            CriteriaOperator sessionIDCriteria = new BinaryOperator("SessionID", GID, BinaryOperatorType.Equal);
            CriteriaOperator criteria = CriteriaOperator.Or(gidCriteria, sessionIDCriteria);

            XPCollection xpCollection = new XPCollection(typeof (XPSecurityUser), criteria);
            if (xpCollection.Count == 1)
            {
                XPSecurityUser securityUser = (XPSecurityUser) xpCollection[0];
                if (userIsOnline) UpdateLastActivity(securityUser);
                return securityUser;
            }
            return null;
        }

        #endregion

        #region GetUser(IToken token, bool online)

        /// <summary>
        /// Gets the user.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <param name="online">if set to <c>true</c> [online].</param>
        /// <returns></returns>
        public ISecurityUser GetUser(IToken token, bool online)
        {
            if (token is GuidToken)
            {
                GuidToken guidToken = (GuidToken) token;
                return GetUser(guidToken.innerGuid, online);
            }
            throw new ArgumentOutOfRangeException("token", "Unknown token type!");
        }

        #endregion

        #region GetUser(string userName, bool userIsOnline)

        /// <summary>
        /// Gets the user.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="userIsOnline">if set to <c>true</c> [user is online].</param>
        /// <returns></returns>
        public ISecurityUser GetUser(string userName, bool userIsOnline)
        {
            GroupOperator criteria = new GroupOperator();
            criteria.Operands.Add(new BinaryOperator("UserName", userName, BinaryOperatorType.Equal));
            XPCollection xpCollection = new XPCollection(typeof (XPSecurityUser), criteria);
            if (xpCollection.Count == 1)
            {
                XPSecurityUser securityUser = (XPSecurityUser) xpCollection[0];
                if (userIsOnline) UpdateLastActivity(securityUser);
                return securityUser;
            }
            return null;
        }

        #endregion

        #region GetUsers(string[] userNames)

        /// <summary>
        /// Gets the users.
        /// </summary>
        /// <param name="userNames">The user names.</param>
        /// <returns></returns>
        public ISecurityUser[] GetUsers(string[] userNames)
        {
            List<ISecurityUser> securityUsers = new List<ISecurityUser>();
            foreach (string userName in userNames)
            {
                ISecurityUser securityUser = GetUser(userName, false);
                if (securityUser != null)
                    securityUsers.Add(securityUser);
            }
            return securityUsers.ToArray();
        }

        #endregion

        #region GetAllUsers

        /// <summary>
        /// Gets all users.
        /// </summary>
        /// <returns></returns>
        public ISecurityUser[] GetAllUsers()
        {
            XPCollection<XPSecurityUser> xpCollection = new XPCollection<XPSecurityUser>();
            ISecurityUser[] securityUsers = new ISecurityUser[xpCollection.Count];
            int length = securityUsers.Length;
            while (length > 0)
            {
                securityUsers[securityUsers.Length - length] = xpCollection[securityUsers.Length - length];
                length--;
            }
            return securityUsers;
        }

        #endregion

        #region UpdateLastActivity

        /// <summary>
        /// Updates the last activity.
        /// </summary>
        /// <param name="user">The user.</param>
        private void UpdateLastActivity(XPSecurityUser user)
        {
            user.LastActivityDate = DateTime.Now;
            user.Save();
        }

        #endregion

        #region GetUserNameByEmail

        /// <summary>
        /// Gets the user name by email.
        /// </summary>
        /// <param name="email">The email.</param>
        /// <returns></returns>
        public string GetUserNameByEmail(string email)
        {
            GroupOperator criteria = new GroupOperator();
            criteria.Operands.Add(new BinaryOperator("Email", email, BinaryOperatorType.Equal));
            XPCollection xpCollection = new XPCollection(typeof (XPSecurityUser), criteria);
            //xpCollection.Reload();
            if (xpCollection.Count > 0)
            {
                XPSecurityUser securityUser = (XPSecurityUser) xpCollection[0];
                return securityUser.UserName;
            }
            return null;
        }

        #endregion

        #region UpdateUser

        /// <summary>
        /// Updates the user.
        /// </summary>
        /// <param name="user">The user.</param>
        public void UpdateUser(ISecurityUser user)
        {
            XPSecurityUser securityUser = (XPSecurityUser) user;
            if (securityUser == null)
                throw new ArrayTypeMismatchException("Argument should be type of XPSecurityUser!");

            Session session = Session.DefaultSession;
            if (securityUser.Session != null)
                session = securityUser.Session;

            session.BeginTransaction();
            try
            {

                securityUser.Save();
                session.CommitTransaction();
            }
            catch (Exception ex)
            {
                session.RollbackTransaction();
                securityUser.Reload();
                throw;
            }
        }

        #endregion

        #region UserExists

        /// <summary>
        /// Users the exists.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <returns></returns>
        public bool UserExists(string userName)
        {
            ISecurityUser securityUser = GetUser(userName, false);
            return (securityUser != null);
        }

        #endregion

        #region DeleteUser

        /// <summary>
        /// Deletes the user.
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="deleteAllRelatedData">if set to <c>true</c> [delete all related data].</param>
        /// <returns></returns>
        public bool DeleteUser(string userName, bool deleteAllRelatedData)
        {
            XPSecurityUser securityUser = (XPSecurityUser) GetUser(userName, false);
            if (securityUser == null)
                return false;
            else
            {
                securityUser.Delete();
                securityUser.Session.PurgeDeletedObjects();
                return true;
            }
        }

        #endregion

        #region Clear

        /// <summary>
        /// Clears this instance.
        /// </summary>
        public void Clear()
        {
            XPCollection<XPSecurityUser> xpCollection = new XPCollection<XPSecurityUser>();
            xpCollection.Session.Delete(xpCollection);
            xpCollection.Session.PurgeDeletedObjects();
        }

        #endregion
    }
}